#include <iostream>
#include <stdio.h>

using std::cout;
using std::endl;

//虚函数指针(指向)---->虚表(存放)---->各个虚函数的地址
//对于非虚拟的单继承，相同派生类D的不同对象d1、d2，它们虚函数表是唯一的，位于只读段

class Base{
public:
    Base(long base=0): _base(base){
        cout<<"Base(long = 0)"<<endl;
    }

    ~Base(){
        cout<<"~Base()"<<endl;
    }

    virtual 
        void func(){
            cout<<"virtual void Base::func()"<<endl;
        }

    virtual 
        void gunc(){
            cout<<"virtual void Base::gunc()"<<endl;
        }
    virtual 
        void hunc(){
            cout<<"virtual void Base::hunc()"<<endl;
        }

private:
    long _base;
};

class Deriver
:public Base
{
public:
    Deriver(long base=0,long deriver=0): Base(base), _deriver(deriver){
        cout<<"Deriver(long = 0, long = 0)"<<endl;
    }

    ~Deriver(){
        cout<<"~Deriver()"<<endl;
    }

    virtual 
        void func(){
            cout<<"virtual void Deriver::func()"<<endl;
        }

    virtual 
        void gunc(){
            cout<<"virtual void Deriver::gunc()"<<endl;
        }
    virtual 
        void hunc(){
            cout<<"virtual void Deriver::hunc()"<<endl;
        }
private:
    int _deriver;
};

void test(){
    Deriver d(111,222);
    //printf("栈对象d的地址：%p\n",&d);
    printf("栈对象d的地址：%p\n",(long*)&d);
    printf("只读段，d的虚表的地址：%p\n",(long*)*(long*)&d);
    //因为d地址的入口就是虚函数指针，对它*得到虚表地址，本质上就是*&d
    
    printf("d的第一个虚函数的地址：%p\n",(long*)*(long*)*(long*)&d);

    cout<<endl;
    Deriver d2(333,444);
    printf("栈对象d2的地址：%p\n",(long*)&d2);
    printf("只读段，d2的虚表的地址：%p\n",(long*)*(long*)&d2);
    printf("d2的第一个虚函数的地址：%p\n",(long*)*(long*)*(long*)&d2);
    //d2和d是不同对象，它俩的地址不同（即他俩拿着的虚函数指针不同）
    //但是它们的虚表地址相同，因为它们的虚函数指针指向的都是同一份虚表

    
    cout<<endl;
    typedef void (*pFunc)();
    //  typedef void(*)() pFunc 
    //  上面等价于这一行，相当于把 指向void类型函数的函数指针 重命名为pFunc，
    //  只是语法上不支持
    pFunc f = (pFunc)*(     (long*)*(long*)&d   );
    //  虚表地址 + * ，得到第一个虚函数的地址
    //  注意函数指针，本质还是指针，指向的是地址而非函数本身
    //  即 f = &func  或  f= func
    printf("第一个虚函数的地址：%p\n",f);
    printf("函数指针的地址，和前一行不同，因为它是指针：%p\n",&f);
    f();    //调用了func

    cout<<endl;
    //pFunc g = (pFunc)*(   (long*) *((long*)&d + 1 )  );
    //虚函数指针地址+1，没意义，那里存放的可能是数据成员_base
    pFunc g = (pFunc)*(   (long*) *(long*)&d + 1  );
    printf("第二个虚函数的地址：%p\n",g);
    g();    //调用了gunc

    cout<<endl;
    pFunc h = (pFunc)*(     (long*) *(long*)&d + 2   );
    printf("第三个虚函数的地址：%p\n",h);
    h();    //调用了hunc


    //直接用地址访问数据成员
    //如果数据成员是int，long类型不同，则操作指针偏移时要注意+1还是+其他
    cout<<endl;
    cout<<"_base="<<  (long) *  ((long*)&d +1)  <<endl;
    cout<<"_deriver="<<  (int) *  ((long*)&d +2)  <<endl;
    //cout<<"_deriver="<<  (long) *  ((long*)&d +2)  <<endl;
    //写成long则会越过int的4位，得到乱码而不是222
}

int main()
{   
    test();
    return 0;
}

